Exploratory Data Analysis (EDA) - Generate questions about your data. - Search for answers by visualising, transforming, and modelling your data. - Use what you learn to refine your questions and/or generate new questions.
library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages ----------------------------------------------------
filter(): dplyr, stats
lag(): dplyr, stats
You can loosely word these questions as: What type of variation occurs within my variables? What type of covariation occurs between my variables?
- A variable is a quantity, quality, or property that you can measure.
- A value is the state of a variable when you measure it. The value of a variable may change from measurement to measurement.
- An observation is a set of measurements made under similar conditions (you usually make all of the measurements in an observation at the same time and on the same object). An observation will contain several values, each associated with a different variable. I’ll sometimes refer to an observation as a data point.
- Tabular data is a set of values, each associated with a variable and an observation. Tabular data is tidy if each value is placed in its own “cell”, each variable in its own column, and each observation in its own row.
Variation is the tendency of the values of a variable to change from measurement to measurement. The best way to understand that pattern is to visualise the distribution of the variable’s values.
diamonds
To examine the distribution of a categorical variable, use a bar chart:
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut))

diamonds %>%
count(cut)
To examine the distribution of a continuous variable, use a histogram:
ggplot(data = diamonds) +
geom_histogram(mapping = aes(x = carat), binwidth = 0.5)

diamonds %>%
count(cut_width(carat, 0.5))
smaller <- diamonds %>%
filter(carat < 3)
ggplot(data = smaller, mapping = aes(x = carat)) +
geom_histogram(binwidth = 0.1)

ggplot(data = smaller, mapping = aes(x = carat, colour = cut)) +
geom_freqpoly(binwidth = 0.1)

Some questions that could be asked: - Which values are the most common? Why? - Which values are rare? Why? Does that match your expectations? - Can you see any unusual patterns? What might explain them?
ggplot(data = smaller, mapping = aes(x = carat)) +
geom_histogram(binwidth = 0.01)

As an example, the histogram above suggests several interesting questions: - Why are there more diamonds at whole carats and common fractions of carats? - Why are there more diamonds slightly to the right of each peak than there are slightly to the left of each peak? - Why are there no diamonds bigger than 3 carats?
Clusters of similar values suggest that subgroups exist in your data. To understand the subgroups, ask: - How are the observations within each cluster similar to each other? - How are the observations in separate clusters different from each other? - How can you explain or describe the clusters? - Why might the appearance of clusters be misleading?
ggplot(data = faithful, mapping = aes(x = eruptions)) +
geom_histogram(binwidth = 0.25)

Discover outliers
ggplot(diamonds) +
geom_histogram(mapping = aes(x = y), binwidth = 0.5)

Zoom in for outliers:
ggplot(diamonds) +
geom_histogram(mapping = aes(x = y), binwidth = 0.5) +
coord_cartesian(ylim = c(0, 50))

(unusual <- diamonds %>%
filter(y < 3 | y > 20) %>%
select(price, x, y, z) %>%
arrange(y))
Unreasonable data, probably error in entering them.
Exercise P90
1
LOOK AT THE DATA FIRST!!!
?diamonds
ggplot(data = diamonds, mapping = aes(x = x)) +
geom_histogram(binwidth = 0.25)

ggplot(data = diamonds, mapping = aes(x = y)) +
geom_histogram(binwidth = 0.25)

ggplot(data = diamonds, mapping = aes(x = z)) +
geom_histogram(binwidth = 0.25)

NA
There are peaks, range: x
library(ggplot2)
ggplot(data = diamonds) +
xlim(c(0,10))+
geom_histogram(binwidth = 0.25, mapping = aes(x = x), fill= "green") +
geom_histogram(binwidth = 0.25, mapping = aes(x = y), fill = "yellow") +
geom_histogram(binwidth = 0.25, mapping = aes(x = z), fill = "blue")

xyz_diamonds <- filter(diamonds, x<20, y<20, z<20)
ggplot(data = xyz_diamonds) +
geom_freqpoly(binwidth = 0.2, mapping = aes(x = x), color = "blue") +
geom_freqpoly(binwidth = 0.2, mapping = aes(x = y), color = "green") +
geom_freqpoly(binwidth = 0.2, mapping = aes(x = z), color = "orange")

2
ggplot(data = diamonds, mapping = aes(x = price)) +
geom_histogram(binwidth = 100)

ggplot(data = diamonds, mapping = aes(x = price)) +
geom_histogram(binwidth = 500)

ggplot(data = diamonds, mapping = aes(x = price)) +
geom_histogram(binwidth = 1000)

ggplot(data = diamonds, mapping = aes(x = price)) +
geom_histogram(binwidth = 2000)

diamonds_price <- filter(diamonds, price<2000)
ggplot(data = diamonds_price, mapping = aes(x = price)) +
geom_histogram(binwidth = 50)

There is a gap at the price around 1500.
3
(diamonds_99<- filter(diamonds, carat == 0.99))
(diamonds_1<- filter(diamonds, carat == 1))
(diamonds_carat <- filter(diamonds, carat == 0.99|carat == 1))
diamonds_carat %>%
count(cut_width(carat, 0.01))
People tend to make diamonds of 1 carat… Price vs. carat
4
ggplot(diamonds) +
geom_histogram(mapping = aes(x = y), binwidth = 0.5)

ggplot(diamonds) +
geom_histogram(mapping = aes(x = y), binwidth = 0.5) +
coord_cartesian(ylim = c(0, 50))

ggplot(diamonds) +
geom_histogram(mapping = aes(x = y), binwidth = 0.5) +
coord_cartesian(xlim = c(0, 10))

ggplot(diamonds) +
geom_histogram(mapping = aes(x = y)) +
coord_cartesian(xlim = c(0, 10))

#when you select a interval not good choice for automatic bin number..
ggplot(diamonds) +
geom_histogram(mapping = aes(x = y), binwidth = 0.2) +
coord_cartesian(xlim = c(0, 10))

ggplot(diamonds) +
geom_histogram(mapping = aes(x = y)) +
coord_cartesian(xlim = c(3.5, 4.5))

ggplot(data = diamonds, mapping = aes(x = price)) +
geom_histogram(binwidth = 100)

ggplot(data = diamonds, mapping = aes(x = price))+
geom_histogram()

ggplot(data = diamonds, mapping = aes(x = x))+
geom_histogram()

R automatically picks a value so that there are 30 bins?
For unusual values
diamonds2 <- diamonds %>%
mutate(y = ifelse(y < 3 | y > 20, NA, y))
diamonds2
ifelse() has three arguments. The first argument test should be a logical vector. The result will contain the value of the second argument, yes, when test is TRUE, and the value of the third argument, no, when it is false.
ggplot(data = diamonds2, mapping = aes(x = x, y = y)) +
geom_point()

#it gives a warning..
ggplot(data = diamonds2, mapping = aes(x = x, y = y)) +
geom_point(na.rm = TRUE)

nycflights13::flights %>%
mutate(
cancelled = is.na(dep_time),
sched_hour = sched_dep_time %/% 100,
sched_min = sched_dep_time %% 100,
sched_dep_time = sched_hour + sched_min / 60
) %>%
ggplot(mapping = aes(sched_dep_time)) +
geom_freqpoly(mapping = aes(colour = cancelled), binwidth = 1/4)

Exercise P93
1
ggplot(data = diamonds2, mapping = aes(x = y)) +
geom_bar()

ggplot(data = diamonds2, mapping = aes(x = y)) +
geom_bar(na.rm = TRUE)

ggplot(data = diamonds2, mapping = aes(x = y)) +
geom_histogram()

ggplot(data = diamonds2, mapping = aes(x = y)) +
geom_histogram(na.rm = TRUE)

2
mean(y)
[1] NA
mean(y, na.rm = TRUE)
[1] 12.63907
sum(y)
[1] NA
sum(y, na.rm = TRUE)
[1] 4152200
Covariation is the tendency for the values of two or more variables to vary together in a related way.
The default appearance of geom_freqpoly() is not that useful for that sort of comparison because the height is given by the count. That means if one of the groups is much smaller than the others, it’s hard to see the differences in shape.
ggplot(data = diamonds, mapping = aes(x = price)) +
geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)

#to see overall difference
ggplot(diamonds) +
geom_bar(mapping = aes(x = cut))

To display density
ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) +
geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
geom_boxplot()

ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
geom_boxplot()

order the data
ggplot(data = mpg) +
geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))

ggplot(data = mpg) +
geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy)) +
coord_flip()

Exercise P90
1
nycflights13::flights
nycflights13::flights %>%
mutate(
Cancelled = is.na(dep_time),
sched_hour = sched_dep_time %/% 100,
sched_min = sched_dep_time %% 100,
sched_dep_time = sched_hour + sched_min / 60
) %>%
ggplot(mapping = aes(x = sched_dep_time, y = ..density..)) +
geom_freqpoly(mapping = aes(colour = Cancelled), binwidth = 1/4) +
ylab("Density") +
xlab("Schedules departure time (hour)")

2
diamonds
?diamonds
ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
geom_point() +
geom_smooth()

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
geom_boxplot()

ggplot(data = diamonds, mapping = aes(x = color, y = price)) +
geom_boxplot()

ggplot(data = diamonds, mapping = aes(x = clarity, y = price)) +
geom_boxplot()

ggplot(data = diamonds, mapping = aes(x = depth, y = price)) +
geom_point() +
geom_smooth()

ggplot(data = diamonds, mapping = aes(x = table, y = price)) +
geom_point() +
geom_smooth()

ggplot(data = xyz_diamonds, mapping = aes(y = price)) +
geom_point(mapping = aes(x = x), color = "green", alpha = 0.1) +
geom_point(mapping = aes(x = y), color = "yellow", alpha = 0.1) +
geom_point(mapping = aes(x = z), color = "black", alpha = 0.1)

The is correlation between carat and size with price, but the most important to predict price seems to be color.
ggplot(data = diamonds, mapping = aes(x = carat, y = price, color = cut)) +
geom_point()

Diamonds with fair cut but bigger value of carat still get a very high price.There’s few diamonds with ideal cut at bigger carat. And at that range the values seem not correlated. ###3
library(ggstance)
Attaching package: 'ggstance'
The following objects are masked from 'package:ggplot2':
GeomErrorbarh, geom_errorbarh
ggplot(data = mpg) +
geom_boxplot(mapping = aes(x = class, y = hwy)) +
coord_flip()

ggplot(data = mpg) +
geom_boxploth(mapping = aes(x = hwy, y = class))

Exactly the same?? ###4
library(lvplot)
ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
geom_lv()
Error: GeomLv was built with an incompatible version of ggproto.
Please reinstall the package that provides this extension.
5
ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
geom_violin()

ggplot(data = diamonds, mapping = aes(x = price)) +
geom_histogram() +
facet_wrap(~cut)

ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) +
geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)

Violin: see the trend of each category clearly Hist & facet: can also compare the number Frequency: compare shapes directly because they overlap ggbeeswarm
6
library(ggbeeswarm)
ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
geom_point()

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
geom_jitter()

ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))

ggplot(data = mpg) +
geom_jitter(mapping = aes(x = displ, y = hwy))

ggplot(data = mpg) +
geom_beeswarm(mapping = aes(x = displ, y = hwy))

ggplot(data = mpg) +
geom_beeswarm(mapping = aes(x = displ, y = hwy), groupOnX = F)

Two categorical variables
ggplot(data = diamonds) +
geom_count(mapping = aes(x = cut, y = color))

diamonds %>%
count(color, cut)
diamonds %>%
count(color, cut) %>%
ggplot(mapping = aes(x = color, y = cut)) +
geom_tile(mapping = aes(fill = n))

Exercise P101
1
library(viridis)
diamonds %>%
count(color, cut) %>%
group_by(color) %>%
mutate(prop = n / sum(n)) %>%
ggplot(mapping = aes(x = color, y = cut)) +
geom_tile(mapping = aes(fill = prop)) +
scale_fill_viridis(limits = c(0, 1))

2
nycflights13::flights
nycflights13::flights %>%
group_by(dest, month) %>%
summarise(avg_delay = mean(dep_delay, na.rm = T)) %>%
ggplot(mapping = aes(x = dest, y = month)) +
geom_tile(mapping = aes(fill = avg_delay))

The destination overlaps. Missing values.
nycflights13::flights %>%
group_by(dest) %>%
summarise(avg_delay = mean(dep_delay, na.rm = T))
nycflights13::flights %>%
filter(dest == dest[c(10:20)]) %>%
group_by(dest, month) %>%
summarise(avg_delay = mean(dep_delay, na.rm = T)) %>%
ggplot(mapping = aes(x = factor(month), y = dest)) +
geom_tile(mapping = aes(fill = avg_delay)) +
labs(x = "Month", y = "Destination", fill = "Departure Delay")

3
diamonds %>%
count(color, cut) %>%
ggplot(mapping = aes(x = cut, y = color)) +
geom_tile(mapping = aes(fill = n))

diamonds %>%
count(color, cut) %>%
ggplot(mapping = aes(x = color, y = cut)) +
geom_tile(mapping = aes(fill = n))

Because colors can not be compared but cut can? So easier to understand…
Two continuous variables
ggplot(data = diamonds) +
geom_point(mapping = aes(x = carat, y = price))

Data overlaps a lot.
ggplot(data = diamonds) +
geom_point(mapping = aes(x = carat, y = price), alpha = 1 / 100)

library(hexbin)
ggplot(data = smaller) +
geom_bin2d(mapping = aes(x = carat, y = price))

ggplot(data = smaller) +
geom_hex(mapping = aes(x = carat, y = price))

ggplot(data = smaller, mapping = aes(x = carat, y = price)) +
geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))

Make the width of the boxplot proportional to the number of points
ggplot(data = smaller, mapping = aes(x = carat, y = price)) +
geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)), varwidth = TRUE)

Display approximately the same number of points in each bin.
ggplot(data = smaller, mapping = aes(x = carat, y = price)) +
geom_boxplot(mapping = aes(group = cut_number(carat, 20)))

Exercise P104
1
ggplot(data = smaller, mapping = aes(x = price,y = ..density..)) +
geom_freqpoly(mapping = aes(color = cut_number(carat, 10)))

ggplot(data = smaller, mapping = aes(x = price, y = ..density..)) +
geom_freqpoly(mapping = aes(color = cut_width(carat, 0.5)))

Two methods yeild very different graph. Cut_number same as its density graph. ###2
ggplot(data = smaller, mapping = aes(x = price, y = carat)) +
geom_boxplot(mapping = aes(group = cut_number(price, 20)))

ggplot(data = smaller, mapping = aes(x = price, y = carat)) +
geom_boxplot(mapping = aes(group = cut_width(price, 1000,boundary = 0)))

3
It seems that when the price is high, the price doesn’t correlate to the size anymore. Maybe some other factors influences.
4
diamonds %>%
#filter(carat<3) %>%
ggplot(mapping = aes(x = carat, y = cut)) +
geom_tile(mapping = aes(fill = price))

Price correlate with carat more than cut.
diamonds %>%
#filter(carat<3) %>%
ggplot(mapping = aes(x = carat, y = price)) +
geom_point(alpha = 0.5) +
facet_wrap(~cut)

More diamonds with large size for fair cut. Other than that not much difference between the different cut.
5
ggplot(data = diamonds) +
geom_point(mapping = aes(x = x, y = y)) +
coord_cartesian(xlim = c(4, 11), ylim = c(4, 11))

ggplot(data = diamonds) +
geom_point(mapping = aes(x = x, y = y)) +
coord_cartesian(xlim = c(4, 11), ylim = c(4, 11))
Because outliers are not shown in binned plots.
Patterns and models
Could this pattern be due to coincidence (i.e. random chance)? How can you describe the relationship implied by the pattern? How strong is the relationship implied by the pattern? What other variables might affect the relationship? Does the relationship change if you look at individual subgroups of the data?
ggplot(data = faithful) +
geom_point(mapping = aes(x = eruptions, y = waiting))

If you think of variation as a phenomenon that creates uncertainty, covariation is a phenomenon that reduces it.
library(modelr)
mod <- lm(log(price) ~ log(carat), data = diamonds)
diamonds2 <- diamonds %>%
add_residuals(mod) %>%
mutate(resid = exp(resid))
ggplot(data = diamonds2) +
geom_point(mapping = aes(x = carat, y = resid))

relative to their size, better quality diamonds are more expensive.
ggplot(data = diamonds2) +
geom_boxplot(mapping = aes(x = cut, y = resid))

ggplot(data = faithful, mapping = aes(x = eruptions)) +
geom_freqpoly(binwidth = 0.25)

ggplot(faithful, aes(eruptions)) +
geom_freqpoly(binwidth = 0.25)

http://www.cookbook-r.com/Graphs/
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpCi0gR2VuZXJhdGUgcXVlc3Rpb25zIGFib3V0IHlvdXIgZGF0YS4KLSBTZWFyY2ggZm9yIGFuc3dlcnMgYnkgdmlzdWFsaXNpbmcsIHRyYW5zZm9ybWluZywgYW5kIG1vZGVsbGluZyB5b3VyIGRhdGEuCi0gVXNlIHdoYXQgeW91IGxlYXJuIHRvIHJlZmluZSB5b3VyIHF1ZXN0aW9ucyBhbmQvb3IgZ2VuZXJhdGUgbmV3IHF1ZXN0aW9ucy4KYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKWW91IGNhbiBsb29zZWx5IHdvcmQgdGhlc2UgcXVlc3Rpb25zIGFzOgpXaGF0IHR5cGUgb2YgdmFyaWF0aW9uIG9jY3VycyB3aXRoaW4gbXkgdmFyaWFibGVzPwpXaGF0IHR5cGUgb2YgY292YXJpYXRpb24gb2NjdXJzIGJldHdlZW4gbXkgdmFyaWFibGVzPwoKLSBBIHZhcmlhYmxlIGlzIGEgcXVhbnRpdHksIHF1YWxpdHksIG9yIHByb3BlcnR5IHRoYXQgeW91IGNhbiBtZWFzdXJlLgotIEEgdmFsdWUgaXMgdGhlIHN0YXRlIG9mIGEgdmFyaWFibGUgd2hlbiB5b3UgbWVhc3VyZSBpdC4gVGhlIHZhbHVlIG9mIGEgdmFyaWFibGUgbWF5IGNoYW5nZSBmcm9tIG1lYXN1cmVtZW50IHRvIG1lYXN1cmVtZW50LgotIEFuIG9ic2VydmF0aW9uIGlzIGEgc2V0IG9mIG1lYXN1cmVtZW50cyBtYWRlIHVuZGVyIHNpbWlsYXIgY29uZGl0aW9ucyAoeW91IHVzdWFsbHkgbWFrZSBhbGwgb2YgdGhlIG1lYXN1cmVtZW50cyBpbiBhbiBvYnNlcnZhdGlvbiBhdCB0aGUgc2FtZSB0aW1lIGFuZCBvbiB0aGUgc2FtZSBvYmplY3QpLiBBbiBvYnNlcnZhdGlvbiB3aWxsIGNvbnRhaW4gc2V2ZXJhbCB2YWx1ZXMsIGVhY2ggYXNzb2NpYXRlZCB3aXRoIGEgZGlmZmVyZW50IHZhcmlhYmxlLiBJ4oCZbGwgc29tZXRpbWVzIHJlZmVyIHRvIGFuIG9ic2VydmF0aW9uIGFzIGEgZGF0YSBwb2ludC4KLSBUYWJ1bGFyIGRhdGEgaXMgYSBzZXQgb2YgdmFsdWVzLCBlYWNoIGFzc29jaWF0ZWQgd2l0aCBhIHZhcmlhYmxlIGFuZCBhbiBvYnNlcnZhdGlvbi4gVGFidWxhciBkYXRhIGlzIHRpZHkgaWYgZWFjaCB2YWx1ZSBpcyBwbGFjZWQgaW4gaXRzIG93biDigJxjZWxs4oCdLCBlYWNoIHZhcmlhYmxlIGluIGl0cyBvd24gY29sdW1uLCBhbmQgZWFjaCBvYnNlcnZhdGlvbiBpbiBpdHMgb3duIHJvdy4KClZhcmlhdGlvbiBpcyB0aGUgdGVuZGVuY3kgb2YgdGhlIHZhbHVlcyBvZiBhIHZhcmlhYmxlIHRvIGNoYW5nZSBmcm9tIG1lYXN1cmVtZW50IHRvIG1lYXN1cmVtZW50LiBUaGUgYmVzdCB3YXkgdG8gdW5kZXJzdGFuZCB0aGF0IHBhdHRlcm4gaXMgdG8gdmlzdWFsaXNlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHZhcmlhYmxl4oCZcyB2YWx1ZXMuCmBgYHtyfQpkaWFtb25kcwpgYGAKClRvIGV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCB1c2UgYSBiYXIgY2hhcnQ6CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArCiAgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gY3V0KSkKYGBgCmBgYHtyfQpkaWFtb25kcyAlPiUgCiAgY291bnQoY3V0KQpgYGAKVG8gZXhhbWluZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgY29udGludW91cyB2YXJpYWJsZSwgdXNlIGEgaGlzdG9ncmFtOgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKwogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0KSwgYmlud2lkdGggPSAwLjUpCmBgYApgYGB7cn0KZGlhbW9uZHMgJT4lIAogIGNvdW50KGN1dF93aWR0aChjYXJhdCwgMC41KSkKYGBgCmBgYHtyfQpzbWFsbGVyIDwtIGRpYW1vbmRzICU+JSAKICBmaWx0ZXIoY2FyYXQgPCAzKQogIApnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIsIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gc21hbGxlciwgbWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIGNvbG91ciA9IGN1dCkpICsKICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC4xKQpgYGAKU29tZSBxdWVzdGlvbnMgdGhhdCBjb3VsZCBiZSBhc2tlZDoKLSBXaGljaCB2YWx1ZXMgYXJlIHRoZSBtb3N0IGNvbW1vbj8gV2h5PwotIFdoaWNoIHZhbHVlcyBhcmUgcmFyZT8gV2h5PyBEb2VzIHRoYXQgbWF0Y2ggeW91ciBleHBlY3RhdGlvbnM/Ci0gQ2FuIHlvdSBzZWUgYW55IHVudXN1YWwgcGF0dGVybnM/IFdoYXQgbWlnaHQgZXhwbGFpbiB0aGVtPwpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBjYXJhdCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDEpCmBgYApBcyBhbiBleGFtcGxlLCB0aGUgaGlzdG9ncmFtIGFib3ZlIHN1Z2dlc3RzIHNldmVyYWwgaW50ZXJlc3RpbmcgcXVlc3Rpb25zOgotIFdoeSBhcmUgdGhlcmUgbW9yZSBkaWFtb25kcyBhdCB3aG9sZSBjYXJhdHMgYW5kIGNvbW1vbiBmcmFjdGlvbnMgb2YgY2FyYXRzPwotIFdoeSBhcmUgdGhlcmUgbW9yZSBkaWFtb25kcyBzbGlnaHRseSB0byB0aGUgcmlnaHQgb2YgZWFjaCBwZWFrIHRoYW4gdGhlcmUgYXJlIHNsaWdodGx5IHRvIHRoZSBsZWZ0IG9mIGVhY2ggcGVhaz8KLSBXaHkgYXJlIHRoZXJlIG5vIGRpYW1vbmRzIGJpZ2dlciB0aGFuIDMgY2FyYXRzPwoKQ2x1c3RlcnMgb2Ygc2ltaWxhciB2YWx1ZXMgc3VnZ2VzdCB0aGF0IHN1Ymdyb3VwcyBleGlzdCBpbiB5b3VyIGRhdGEuIFRvIHVuZGVyc3RhbmQgdGhlIHN1Ymdyb3VwcywgYXNrOgotIEhvdyBhcmUgdGhlIG9ic2VydmF0aW9ucyB3aXRoaW4gZWFjaCBjbHVzdGVyIHNpbWlsYXIgdG8gZWFjaCBvdGhlcj8KLSBIb3cgYXJlIHRoZSBvYnNlcnZhdGlvbnMgaW4gc2VwYXJhdGUgY2x1c3RlcnMgZGlmZmVyZW50IGZyb20gZWFjaCBvdGhlcj8KLSBIb3cgY2FuIHlvdSBleHBsYWluIG9yIGRlc2NyaWJlIHRoZSBjbHVzdGVycz8KLSBXaHkgbWlnaHQgdGhlIGFwcGVhcmFuY2Ugb2YgY2x1c3RlcnMgYmUgbWlzbGVhZGluZz8KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGZhaXRoZnVsLCBtYXBwaW5nID0gYWVzKHggPSBlcnVwdGlvbnMpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4yNSkKYGBgCgpEaXNjb3ZlciBvdXRsaWVycwpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBiaW53aWR0aCA9IDAuNSkKYGBgClpvb20gaW4gZm9yIG91dGxpZXJzOgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBiaW53aWR0aCA9IDAuNSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCA1MCkpCmBgYApgYGB7cn0KKHVudXN1YWwgPC0gZGlhbW9uZHMgJT4lIAogIGZpbHRlcih5IDwgMyB8IHkgPiAyMCkgJT4lIAogIHNlbGVjdChwcmljZSwgeCwgeSwgeikgJT4lCiAgYXJyYW5nZSh5KSkKYGBgClVucmVhc29uYWJsZSBkYXRhLCBwcm9iYWJseSBlcnJvciBpbiBlbnRlcmluZyB0aGVtLgoKIyNFeGVyY2lzZSBQOTAKIyMjMQpMT09LIEFUIFRIRSBEQVRBIEZJUlNUISEhCmBgYHtyfQo/ZGlhbW9uZHMKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0geCkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1KQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0geSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1KQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0geikpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1KQogIApgYGAKVGhlcmUgYXJlIHBlYWtzLCByYW5nZTogeDx6PHkKcmFuZ2U7IG91dGxpZXJzCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArIAogIHhsaW0oYygwLDEwKSkrCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1LCBtYXBwaW5nID0gYWVzKHggPSB4KSwgZmlsbD0gImdyZWVuIikgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4yNSwgbWFwcGluZyA9IGFlcyh4ID0geSksIGZpbGwgPSAieWVsbG93IikgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4yNSwgbWFwcGluZyA9IGFlcyh4ID0geiksIGZpbGwgPSAiYmx1ZSIpCmBgYAoKCmBgYHtyfQp4eXpfZGlhbW9uZHMgPC0gZmlsdGVyKGRpYW1vbmRzLCB4PDIwLCB5PDIwLCB6PDIwKQpnZ3Bsb3QoZGF0YSA9IHh5el9kaWFtb25kcykgKwogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjIsIG1hcHBpbmcgPSBhZXMoeCA9IHgpLCBjb2xvciA9ICJibHVlIikgKwogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjIsIG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBjb2xvciA9ICJncmVlbiIpICsKICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC4yLCBtYXBwaW5nID0gYWVzKHggPSB6KSwgY29sb3IgPSAib3JhbmdlIikKYGBgCiMjIzIKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwMCkKCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUwMCkKCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwMDApCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyMDAwKQpgYGAKYGBge3J9CmRpYW1vbmRzX3ByaWNlIDwtIGZpbHRlcihkaWFtb25kcywgcHJpY2U8MjAwMCkKZ2dwbG90KGRhdGEgPSBkaWFtb25kc19wcmljZSwgbWFwcGluZyA9IGFlcyh4ID0gcHJpY2UpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTApCmBgYApUaGVyZSBpcyBhIGdhcCBhdCB0aGUgcHJpY2UgYXJvdW5kIDE1MDAuCgojIyMzCmBgYHtyfQooZGlhbW9uZHNfOTk8LSBmaWx0ZXIoZGlhbW9uZHMsIGNhcmF0ID09IDAuOTkpKQooZGlhbW9uZHNfMTwtIGZpbHRlcihkaWFtb25kcywgY2FyYXQgPT0gMSkpCgooZGlhbW9uZHNfY2FyYXQgPC0gZmlsdGVyKGRpYW1vbmRzLCBjYXJhdCA9PSAwLjk5fGNhcmF0ID09IDEpKQpkaWFtb25kc19jYXJhdCAlPiUKICBjb3VudChjdXRfd2lkdGgoY2FyYXQsIDAuMDEpKQpgYGAKUGVvcGxlIHRlbmQgdG8gbWFrZSBkaWFtb25kcyBvZiAxIGNhcmF0Li4uIApQcmljZSB2cy4gY2FyYXQKCiMjIzQKYGBge3J9CmdncGxvdChkaWFtb25kcykgKyAKICBnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKHggPSB5KSwgYmlud2lkdGggPSAwLjUpIAoKZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBiaW53aWR0aCA9IDAuNSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCA1MCkpCgpnZ3Bsb3QoZGlhbW9uZHMpICsgCiAgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyh4ID0geSksIGJpbndpZHRoID0gMC41KSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEwKSkKCmdncGxvdChkaWFtb25kcykgKyAKICBnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKHggPSB5KSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxMCkpCiN3aGVuIHlvdSBzZWxlY3QgYSBpbnRlcnZhbCBub3QgZ29vZCBjaG9pY2UgZm9yIGF1dG9tYXRpYyBiaW4gbnVtYmVyLi4KZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpLCBiaW53aWR0aCA9IDAuMikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxMCkpCgoKZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IHkpKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDMuNSwgNC41KSkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDApCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSkpKyAKICBnZW9tX2hpc3RvZ3JhbSgpCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSB4KSkrIAogIGdlb21faGlzdG9ncmFtKCkKYGBgClIgYXV0b21hdGljYWxseSBwaWNrcyBhIHZhbHVlIHNvIHRoYXQgdGhlcmUgYXJlIDMwIGJpbnM/CgoKRm9yIHVudXN1YWwgdmFsdWVzIApgYGB7cn0KZGlhbW9uZHMyIDwtIGRpYW1vbmRzICU+JSAKICBtdXRhdGUoeSA9IGlmZWxzZSh5IDwgMyB8IHkgPiAyMCwgTkEsIHkpKQpkaWFtb25kczIKYGBgCmlmZWxzZSgpIGhhcyB0aHJlZSBhcmd1bWVudHMuIFRoZSBmaXJzdCBhcmd1bWVudCB0ZXN0IHNob3VsZCBiZSBhIGxvZ2ljYWwgdmVjdG9yLiBUaGUgcmVzdWx0IHdpbGwgY29udGFpbiB0aGUgdmFsdWUgb2YgdGhlIHNlY29uZCBhcmd1bWVudCwgeWVzLCB3aGVuIHRlc3QgaXMgVFJVRSwgYW5kIHRoZSB2YWx1ZSBvZiB0aGUgdGhpcmQgYXJndW1lbnQsIG5vLCB3aGVuIGl0IGlzIGZhbHNlLgoKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMyLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0geSkpICsgCiAgZ2VvbV9wb2ludCgpCiNpdCBnaXZlcyBhIHdhcm5pbmcuLgpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMyLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0geSkpICsgCiAgZ2VvbV9wb2ludChuYS5ybSA9IFRSVUUpCmBgYApgYGB7cn0KbnljZmxpZ2h0czEzOjpmbGlnaHRzICU+JSAKICBtdXRhdGUoCiAgICBjYW5jZWxsZWQgPSBpcy5uYShkZXBfdGltZSksCiAgICBzY2hlZF9ob3VyID0gc2NoZWRfZGVwX3RpbWUgJS8lIDEwMCwKICAgIHNjaGVkX21pbiA9IHNjaGVkX2RlcF90aW1lICUlIDEwMCwKICAgIHNjaGVkX2RlcF90aW1lID0gc2NoZWRfaG91ciArIHNjaGVkX21pbiAvIDYwCiAgKSAlPiUgCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoc2NoZWRfZGVwX3RpbWUpKSArIAogICAgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNhbmNlbGxlZCksIGJpbndpZHRoID0gMS80KQpgYGAKCiMjRXhlcmNpc2UgUDkzCiMjIzEKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMyLCBtYXBwaW5nID0gYWVzKHggPSB5KSkgKyAKICBnZW9tX2JhcigpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kczIsIG1hcHBpbmcgPSBhZXMoeCA9IHkpKSArIAogIGdlb21fYmFyKG5hLnJtID0gVFJVRSkKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kczIsIG1hcHBpbmcgPSBhZXMoeCA9IHkpKSArIAogIGdlb21faGlzdG9ncmFtKCkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzMiwgbWFwcGluZyA9IGFlcyh4ID0geSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0obmEucm0gPSBUUlVFKQpgYGAKIyMjMgpgYGB7cn0KbWVhbih5KQptZWFuKHksIG5hLnJtID0gVFJVRSkKCnN1bSh5KQpzdW0oeSwgbmEucm0gPSBUUlVFKQpgYGAKCgpDb3ZhcmlhdGlvbiBpcyB0aGUgdGVuZGVuY3kgZm9yIHRoZSB2YWx1ZXMgb2YgdHdvIG9yIG1vcmUgdmFyaWFibGVzIHRvIHZhcnkgdG9nZXRoZXIgaW4gYSByZWxhdGVkIHdheS4gCgpUaGUgZGVmYXVsdCBhcHBlYXJhbmNlIG9mIGdlb21fZnJlcXBvbHkoKSBpcyBub3QgdGhhdCB1c2VmdWwgZm9yIHRoYXQgc29ydCBvZiBjb21wYXJpc29uIGJlY2F1c2UgdGhlIGhlaWdodCBpcyBnaXZlbiBieSB0aGUgY291bnQuIFRoYXQgbWVhbnMgaWYgb25lIG9mIHRoZSBncm91cHMgaXMgbXVjaCBzbWFsbGVyIHRoYW4gdGhlIG90aGVycywgaXTigJlzIGhhcmQgdG8gc2VlIHRoZSBkaWZmZXJlbmNlcyBpbiBzaGFwZS4gCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSkpICsgCiAgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG91ciA9IGN1dCksIGJpbndpZHRoID0gNTAwKQoKI3RvIHNlZSBvdmVyYWxsIGRpZmZlcmVuY2UKZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IGN1dCkpCmBgYApUbyBkaXNwbGF5IGRlbnNpdHkKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlLCB5ID0gLi5kZW5zaXR5Li4pKSArIAogIGdlb21fZnJlcXBvbHkobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjdXQpLCBiaW53aWR0aCA9IDUwMCkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBjbGFzcywgeSA9IGh3eSkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKb3JkZXIgdGhlIGRhdGEKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgRlVOID0gbWVkaWFuKSwgeSA9IGh3eSkpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihjbGFzcywgaHd5LCBGVU4gPSBtZWRpYW4pLCB5ID0gaHd5KSkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKIyNFeGVyY2lzZSBQOTAKIyMjMQpgYGB7cn0KbnljZmxpZ2h0czEzOjpmbGlnaHRzCmBgYApgYGB7cn0KbnljZmxpZ2h0czEzOjpmbGlnaHRzICU+JSAKICBtdXRhdGUoCiAgICBDYW5jZWxsZWQgPSBpcy5uYShkZXBfdGltZSksCiAgICBzY2hlZF9ob3VyID0gc2NoZWRfZGVwX3RpbWUgJS8lIDEwMCwKICAgIHNjaGVkX21pbiA9IHNjaGVkX2RlcF90aW1lICUlIDEwMCwKICAgIHNjaGVkX2RlcF90aW1lID0gc2NoZWRfaG91ciArIHNjaGVkX21pbiAvIDYwCiAgKSAlPiUgCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHNjaGVkX2RlcF90aW1lLCB5ID0gLi5kZW5zaXR5Li4pKSArIAogICAgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG91ciA9IENhbmNlbGxlZCksIGJpbndpZHRoID0gMS80KSArCiAgeWxhYigiRGVuc2l0eSIpICsKICB4bGFiKCJTY2hlZHVsZXMgZGVwYXJ0dXJlIHRpbWUgKGhvdXIpIikKYGBgCiMjIzIKYGBge3J9CmRpYW1vbmRzCj9kaWFtb25kcwpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aCgpCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpICsKICBnZW9tX2JveHBsb3QoKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY29sb3IsIHkgPSBwcmljZSkpICsKICBnZW9tX2JveHBsb3QoKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY2xhcml0eSwgeSA9IHByaWNlKSkgKwogIGdlb21fYm94cGxvdCgpCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBkZXB0aCwgeSA9IHByaWNlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gdGFibGUsIHkgPSBwcmljZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKCkKCmdncGxvdChkYXRhID0geHl6X2RpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHkgPSBwcmljZSkpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHgpLCBjb2xvciA9ICJncmVlbiIsIGFscGhhID0gMC4xKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSB5KSwgY29sb3IgPSAieWVsbG93IiwgYWxwaGEgPSAwLjEpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHopLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC4xKQpgYGAKVGhlIGlzIGNvcnJlbGF0aW9uIGJldHdlZW4gY2FyYXQgYW5kIHNpemUgd2l0aCBwcmljZSwgYnV0IHRoZSBtb3N0IGltcG9ydGFudCB0byBwcmVkaWN0IHByaWNlIHNlZW1zIHRvIGJlIGNvbG9yLgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSwgY29sb3IgPSBjdXQpKSArCiAgZ2VvbV9wb2ludCgpCmBgYApEaWFtb25kcyB3aXRoIGZhaXIgY3V0IGJ1dCBiaWdnZXIgdmFsdWUgb2YgY2FyYXQgc3RpbGwgZ2V0IGEgdmVyeSBoaWdoIHByaWNlLlRoZXJlJ3MgZmV3IGRpYW1vbmRzIHdpdGggaWRlYWwgY3V0IGF0IGJpZ2dlciBjYXJhdC4gQW5kIGF0IHRoYXQgcmFuZ2UgdGhlIHZhbHVlcyBzZWVtIG5vdCBjb3JyZWxhdGVkLgojIyMzCmBgYHtyfQpsaWJyYXJ5KGdnc3RhbmNlKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGNsYXNzLCB5ID0gaHd5KSkgKwogIGNvb3JkX2ZsaXAoKQoKZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2JveHBsb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGh3eSwgeSA9IGNsYXNzKSkKYGBgCkV4YWN0bHkgdGhlIHNhbWU/PwojIyM0CmBgYHtyfQpsaWJyYXJ5KGx2cGxvdCkKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArCiAgZ2VvbV9sdigpCmBgYAojIyM1CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpICsgCiAgZ2VvbV92aW9saW4oKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gcHJpY2UpKSArIAogIGdlb21faGlzdG9ncmFtKCkgKwogIGZhY2V0X3dyYXAofmN1dCkKCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlLCB5ID0gLi5kZW5zaXR5Li4pKSArIAogIGdlb21fZnJlcXBvbHkobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjdXQpLCBiaW53aWR0aCA9IDUwMCkKYGBgClZpb2xpbjogc2VlIHRoZSB0cmVuZCBvZiBlYWNoIGNhdGVnb3J5IGNsZWFybHkKSGlzdCAmIGZhY2V0OiBjYW4gYWxzbyBjb21wYXJlIHRoZSBudW1iZXIKRnJlcXVlbmN5OiBjb21wYXJlIHNoYXBlcyBkaXJlY3RseSBiZWNhdXNlIHRoZXkgb3ZlcmxhcApnZ2JlZXN3YXJtCgojIyM2CmBgYHtyfQpsaWJyYXJ5KGdnYmVlc3dhcm0pCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArIAogIGdlb21fcG9pbnQoKQoKZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArIAogIGdlb21faml0dGVyKCkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQoKZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2ppdHRlcihtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpCgpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fYmVlc3dhcm0obWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQoKZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2JlZXN3YXJtKG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSwgZ3JvdXBPblggPSBGKQpgYGAKIyMgVHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcwpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKwogIGdlb21fY291bnQobWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gY29sb3IpKQpgYGAKYGBge3J9CmRpYW1vbmRzICU+JSAKICBjb3VudChjb2xvciwgY3V0KQojZm9yIGV2ZXJ5IGNvbWJpbmF0aW9uIG9mIGNvbG9yIGFuZCBjb3VudApgYGAKYGBge3J9CmRpYW1vbmRzICU+JSAKICBjb3VudChjb2xvciwgY3V0KSAlPiUgIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBjb2xvciwgeSA9IGN1dCkpICsKICAgIGdlb21fdGlsZShtYXBwaW5nID0gYWVzKGZpbGwgPSBuKSkKYGBgCgojIyBFeGVyY2lzZSBQMTAxCiMjIzEKYGBge3J9CmxpYnJhcnkodmlyaWRpcykKYGBgCmBgYHtyfQpkaWFtb25kcyAlPiUgCiAgY291bnQoY29sb3IsIGN1dCkgJT4lCiAgZ3JvdXBfYnkoY29sb3IpICU+JQogIG11dGF0ZShwcm9wID0gbiAvIHN1bShuKSkgJT4lCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGNvbG9yLCB5ID0gY3V0KSkgKwogIGdlb21fdGlsZShtYXBwaW5nID0gYWVzKGZpbGwgPSBwcm9wKSkgICsKICBzY2FsZV9maWxsX3ZpcmlkaXMobGltaXRzID0gYygwLCAxKSkKYGBgCiMjIzIKYGBge3J9Cm55Y2ZsaWdodHMxMzo6ZmxpZ2h0cwpgYGAKYGBge3J9Cm55Y2ZsaWdodHMxMzo6ZmxpZ2h0cyAlPiUgCiAgZ3JvdXBfYnkoZGVzdCwgbW9udGgpICU+JSAgCiAgc3VtbWFyaXNlKGF2Z19kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFQpKSAlPiUgIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBkZXN0LCB5ID0gbW9udGgpKSArCiAgICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gYXZnX2RlbGF5KSkKYGBgClRoZSBkZXN0aW5hdGlvbiBvdmVybGFwcy4gTWlzc2luZyB2YWx1ZXMuIAoKYGBge3J9Cm55Y2ZsaWdodHMxMzo6ZmxpZ2h0cyAlPiUgCiAgZ3JvdXBfYnkoZGVzdCkgJT4lICAKICBzdW1tYXJpc2UoYXZnX2RlbGF5ID0gbWVhbihkZXBfZGVsYXksIG5hLnJtID0gVCkpCmBgYApgYGB7cn0KbnljZmxpZ2h0czEzOjpmbGlnaHRzICU+JSAKICBmaWx0ZXIoZGVzdCA9PSBkZXN0W2MoMTA6MjApXSkgJT4lCiAgZ3JvdXBfYnkoZGVzdCwgbW9udGgpICU+JSAgCiAgc3VtbWFyaXNlKGF2Z19kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFQpKSAlPiUgIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBmYWN0b3IobW9udGgpLCB5ID0gZGVzdCkpICsKICAgIGdlb21fdGlsZShtYXBwaW5nID0gYWVzKGZpbGwgPSBhdmdfZGVsYXkpKSArCmxhYnMoeCA9ICJNb250aCIsIHkgPSAiRGVzdGluYXRpb24iLCBmaWxsID0gIkRlcGFydHVyZSBEZWxheSIpCmBgYAojIyMzCmBgYHtyfQpkaWFtb25kcyAlPiUgCiAgY291bnQoY29sb3IsIGN1dCkgJT4lICAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gY29sb3IpKSArCiAgICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gbikpCgpkaWFtb25kcyAlPiUgCiAgY291bnQoY29sb3IsIGN1dCkgJT4lICAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY29sb3IsIHkgPSBjdXQpKSArCiAgICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gbikpCmBgYApCZWNhdXNlIGNvbG9ycyBjYW4gbm90IGJlIGNvbXBhcmVkIGJ1dCBjdXQgY2FuPyBTbyBlYXNpZXIgdG8gdW5kZXJzdGFuZC4uLgoKIyNUd28gY29udGludW91cyB2YXJpYWJsZXMKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKQpgYGAKRGF0YSBvdmVybGFwcyBhIGxvdC4KYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSwgYWxwaGEgPSAxIC8gMTAwKQpgYGAKYGBge3J9CmxpYnJhcnkoaGV4YmluKQpgYGAKYGBge3J9CmdncGxvdChkYXRhID0gc21hbGxlcikgKwogIGdlb21fYmluMmQobWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpCgpnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIpICsKICBnZW9tX2hleChtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIsIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIAogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKGdyb3VwID0gY3V0X3dpZHRoKGNhcmF0LCAwLjEpKSkKYGBgCk1ha2UgdGhlIHdpZHRoIG9mIHRoZSBib3hwbG90IHByb3BvcnRpb25hbCB0byB0aGUgbnVtYmVyIG9mIHBvaW50cwpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyAKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyhncm91cCA9IGN1dF93aWR0aChjYXJhdCwgMC4xKSksIHZhcndpZHRoID0gVFJVRSkKYGBgCkRpc3BsYXkgYXBwcm94aW1hdGVseSB0aGUgc2FtZSBudW1iZXIgb2YgcG9pbnRzIGluIGVhY2ggYmluLgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyAKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyhncm91cCA9IGN1dF9udW1iZXIoY2FyYXQsIDIwKSkpCmBgYAoKIyNFeGVyY2lzZSBQMTA0CiMjIzEKYGBge3J9CmdncGxvdChkYXRhID0gc21hbGxlciwgbWFwcGluZyA9IGFlcyh4ID0gcHJpY2UseSA9IC4uZGVuc2l0eS4uKSkgKyAKICBnZW9tX2ZyZXFwb2x5KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBjdXRfbnVtYmVyKGNhcmF0LCAxMCkpKQoKZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSwgeSA9IC4uZGVuc2l0eS4uKSkgKyAKICBnZW9tX2ZyZXFwb2x5KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBjdXRfd2lkdGgoY2FyYXQsIDAuNSkpKQpgYGAKVHdvIG1ldGhvZHMgeWVpbGQgdmVyeSBkaWZmZXJlbnQgZ3JhcGguIEN1dF9udW1iZXIgc2FtZSBhcyBpdHMgZGVuc2l0eSBncmFwaC4KIyMjMgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLCBtYXBwaW5nID0gYWVzKHggPSBwcmljZSwgeSA9IGNhcmF0KSkgKyAKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyhncm91cCA9IGN1dF9udW1iZXIocHJpY2UsIDIwKSkpCgpnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlLCB5ID0gY2FyYXQpKSArIAogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKGdyb3VwID0gY3V0X3dpZHRoKHByaWNlLCAxMDAwLGJvdW5kYXJ5ID0gMCkpKQpgYGAKIyMjMwpJdCBzZWVtcyB0aGF0IHdoZW4gdGhlIHByaWNlIGlzIGhpZ2gsIHRoZSBwcmljZSBkb2Vzbid0IGNvcnJlbGF0ZSB0byB0aGUgc2l6ZSBhbnltb3JlLiBNYXliZSBzb21lIG90aGVyIGZhY3RvcnMgaW5mbHVlbmNlcy4KCiMjIzQKYGBge3J9CmRpYW1vbmRzICU+JSAKICAjZmlsdGVyKGNhcmF0PDMpICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBjdXQpKSArCiAgICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gcHJpY2UpKQpgYGAKUHJpY2UgY29ycmVsYXRlIHdpdGggY2FyYXQgbW9yZSB0aGFuIGN1dC4KYGBge3J9CmRpYW1vbmRzICU+JSAKICAjZmlsdGVyKGNhcmF0PDMpICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArCiAgICBmYWNldF93cmFwKH5jdXQpCmBgYApNb3JlIGRpYW1vbmRzIHdpdGggbGFyZ2Ugc2l6ZSBmb3IgZmFpciBjdXQuIE90aGVyIHRoYW4gdGhhdCBub3QgbXVjaCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGRpZmZlcmVudCBjdXQuCgojIyM1CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0geSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoNCwgMTEpLCB5bGltID0gYyg0LCAxMSkpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHkpKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDQsIDExKSwgeWxpbSA9IGMoNCwgMTEpKQpgYGAKQmVjYXVzZSBvdXRsaWVycyBhcmUgbm90IHNob3duIGluIGJpbm5lZCBwbG90cy4KCiMjUGF0dGVybnMgYW5kIG1vZGVscwpDb3VsZCB0aGlzIHBhdHRlcm4gYmUgZHVlIHRvIGNvaW5jaWRlbmNlIChpLmUuIHJhbmRvbSBjaGFuY2UpPwpIb3cgY2FuIHlvdSBkZXNjcmliZSB0aGUgcmVsYXRpb25zaGlwIGltcGxpZWQgYnkgdGhlIHBhdHRlcm4/CkhvdyBzdHJvbmcgaXMgdGhlIHJlbGF0aW9uc2hpcCBpbXBsaWVkIGJ5IHRoZSBwYXR0ZXJuPwpXaGF0IG90aGVyIHZhcmlhYmxlcyBtaWdodCBhZmZlY3QgdGhlIHJlbGF0aW9uc2hpcD8KRG9lcyB0aGUgcmVsYXRpb25zaGlwIGNoYW5nZSBpZiB5b3UgbG9vayBhdCBpbmRpdmlkdWFsIHN1Ymdyb3VwcyBvZiB0aGUgZGF0YT8KYGBge3J9CmdncGxvdChkYXRhID0gZmFpdGhmdWwpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBlcnVwdGlvbnMsIHkgPSB3YWl0aW5nKSkKYGBgCklmIHlvdSB0aGluayBvZiB2YXJpYXRpb24gYXMgYSBwaGVub21lbm9uIHRoYXQgY3JlYXRlcyB1bmNlcnRhaW50eSwgY292YXJpYXRpb24gaXMgYSBwaGVub21lbm9uIHRoYXQgcmVkdWNlcyBpdC4KCmBgYHtyfQpsaWJyYXJ5KG1vZGVscikKCm1vZCA8LSBsbShsb2cocHJpY2UpIH4gbG9nKGNhcmF0KSwgZGF0YSA9IGRpYW1vbmRzKQoKZGlhbW9uZHMyIDwtIGRpYW1vbmRzICU+JSAKICBhZGRfcmVzaWR1YWxzKG1vZCkgJT4lIAogIG11dGF0ZShyZXNpZCA9IGV4cChyZXNpZCkpCgpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzMikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcmVzaWQpKQpgYGAKCnJlbGF0aXZlIHRvIHRoZWlyIHNpemUsIGJldHRlciBxdWFsaXR5IGRpYW1vbmRzIGFyZSBtb3JlIGV4cGVuc2l2ZS4KYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMyKSArIAogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSByZXNpZCkpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEgPSBmYWl0aGZ1bCwgbWFwcGluZyA9IGFlcyh4ID0gZXJ1cHRpb25zKSkgKyAKICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC4yNSkKCmdncGxvdChmYWl0aGZ1bCwgYWVzKGVydXB0aW9ucykpICsgCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuMjUpCmBgYApodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy8KCg==